//
//  L0PlugSuitFlavor.m
//  PlugSuit
//
//  Created by ∞ on 10/3/07.
//  Copyright 2007 Emanuele Vulcano. All rights reserved.
//

#import "PlSFlavor.h"
#import <Carbon/Carbon.h>
#include <sys/sysctl.h>

static int sysctlbyname_with_pid (const char *name, pid_t pid,
								  void *oldp, size_t *oldlenp,
								  void *newp, size_t newlen)
{
    if (pid == 0) {
        if (sysctlbyname(name, oldp, oldlenp, newp, newlen) == -1)  {
            fprintf(stderr, "sysctlbyname_with_pid(0): sysctlbyname  failed:"
					"%s\n", strerror(errno));
            return -1;
        }
    } else {
        int mib[CTL_MAXNAME];
        size_t len = CTL_MAXNAME;
        if (sysctlnametomib(name, mib, &len) == -1) {
            fprintf(stderr, "sysctlbyname_with_pid: sysctlnametomib  failed:"
					"%s\n", strerror(errno));
            return -1;
        }
        mib[len] = pid;
        len++;
        if (sysctl(mib, len, oldp, oldlenp, newp, newlen) == -1)  {
            fprintf(stderr, "sysctlbyname_with_pid: sysctl  failed:"
                    "%s\n", strerror(errno));
            return -1;
        }
    }
    return 0;
}

static int is_pid_native (pid_t pid)
{
    int ret = 0;
    size_t sz = sizeof(ret);
	
    if (sysctlbyname_with_pid("sysctl.proc_native", pid,
							  &ret, &sz, NULL, 0) == -1) {
		if (errno == ENOENT) {
            return 1;
        }
        fprintf(stderr, "is_pid_native: sysctlbyname_with_pid  failed:"
                "%s\n", strerror(errno));
        return -1;
    }
    return ret;
}

int PlSGetFlavor(pid_t pid) {
	// GetApplicationFlavor returns:
	//     -1 if the application flavor could not be identified
	//      0 if the application is a Mac OS Classic application
	//      2 if the application is a Carbon application
	//      3 if the application is a Cocoa application
	
	if (!is_pid_native(pid))
		return PlSRosettaTranslatedFlavor;
	
	int flavor;
	OSStatus status;
	CFDictionaryRef processInfoDict = NULL;
	CFNumberRef processInfoFlavor = NULL;
	
	ProcessSerialNumber psn;
	status = GetProcessForPID(pid, &psn);
	require_noerr(status, GetCurrentProcess);
	
	processInfoDict = ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
	require(processInfoDict != NULL, ProcessInformationCopyDictionary);
	
	processInfoFlavor = CFDictionaryGetValue(processInfoDict, CFSTR("Flavor"));
	require(processInfoFlavor != NULL, CFDictionaryGetValue);
	
	CFNumberGetValue(processInfoFlavor, kCFNumberIntType, &flavor);
	
CFDictionaryGetValue:
ProcessInformationCopyDictionary:
GetCurrentProcess:
	
	if (processInfoFlavor != NULL)
		CFRelease(processInfoFlavor);
	if (processInfoDict != NULL)
		CFRelease(processInfoDict);
	
	return flavor;
}

BOOL PlSIsCocoaApplication(pid_t pid) {
	return PlSGetFlavor(pid) == PlSCocoaFlavor;
}
